package script

import (
	"fmt"
	"io"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/infraboard/mcube/logger"
	"github.com/infraboard/mcube/logger/zap"
)

func NewExcutor(modules string) *Excutor {
	return &Excutor{
		ModuleDir: modules,
		log:       zap.L(),
	}
}

// 负责执行脚本(模块)
// 执行:   脚本的名称(模块名称), 脚本的参数
// Script Excutor 需要在一个目录下面 搜索脚本, 搜索目录需要提前定义
type Excutor struct {
	ModuleDir string
	log       logger.Logger
}

// moduleName 模块名称
// moduleParams 参数 Json
// 继续的结果, IO 数据流
func (e *Excutor) Exec(moduleName string, moduleParams string, dst io.Writer) error {
	// 1. 寻找模块的绝对路由
	fp, err := e.find(moduleName)
	if err != nil {
		return err
	}

	if fp == "" {
		return fmt.Errorf("module: %s not found", moduleName)
	}

	e.log.Infof("found module path: %s", fp)

	// 2. 执行模块, 并实时返回执行结果,
	// 使用 os/exec 来执行系统命令
	// 根据脚本的扩展名, 来决定如何执行
	var cmd *exec.Cmd
	// 2.1 构造cmd命令
	ext := filepath.Ext(moduleName)
	switch ext {
	case ".sh":
		cmd = exec.Command("bash", fp, moduleParams)
	case ".py":
		cmd = exec.Command("python", fp, moduleParams)
	default:
		// 二进制可执行文件 直接执行
		cmd = exec.Command(fp, moduleParams)
	}

	// 获取命令的输出, 以流的方式
	cmd.Stderr = dst
	cmd.Stdout = dst

	// 执行命令, 后台执行 go cmd.Run
	if err := cmd.Start(); err != nil {
		return err
	}

	return cmd.Wait()
}

func (e *Excutor) find(module string) (string, error) {
	// 获取到存放脚本目录的 绝对路径
	absPath, err := filepath.Abs(e.ModuleDir)
	if err != nil {
		return "", fmt.Errorf("find module %s abs path error %s", module, err)
	}

	// 防止用户传入的执行脚本 超出指定目录
	if strings.Contains(module, "..") {
		return "", fmt.Errorf("module forbiden .. in module")
	}

	// 拼凑脚本绝对路径
	return filepath.Join(absPath, module), nil
}
